package cn.jpanda.elastic.job.autoconfigure.configuration;

import cn.jpanda.elastic.job.autoconfigure.annotations.EnableElasticJob;
import cn.jpanda.elastic.job.autoconfigure.annotations.JobParser;
import cn.jpanda.elastic.job.autoconfigure.annotations.metedata.ElasticJobMeteData;
import cn.jpanda.elastic.job.autoconfigure.constants.ElasticJobAnnotationConstants;
import cn.jpanda.elastic.job.autoconfigure.exception.ElasticJobParserConflictRuntimeException;
import cn.jpanda.elastic.job.autoconfigure.strategy.JobParseAnnotationHandlerStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 扫描{@link ElasticJobMeteData},并装载到Spring Container中。
 *
 * @author Hanqi <jpanda@aliyun.com>
 * @since 2018/9/21 10:17
 */
@Slf4j
public class ElasticJobScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    @Override
    @SuppressWarnings("unchecked")
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取 EnableElasticJob 所有注解参数
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableElasticJob.class.getName()));

        assert annotationAttributes != null;

        //是否开启ElasticJob
        boolean enabled = annotationAttributes.getBoolean(ElasticJobAnnotationConstants.ENABLE_ELASTIC_JOB_ATTR_ENABLE);
        if (!enabled) {
            log.debug("当前Elastic-Job 已被禁用");
            return;
        }

        // 处理包加载路径
        List<String> basePackages = new ArrayList<>();
        basePackages.add(((StandardAnnotationMetadata) importingClassMetadata).getIntrospectedClass().getPackage().getName());

        for (String pkg : annotationAttributes.getStringArray(ElasticJobAnnotationConstants.ENABLE_ELASTIC_JOB_ATTR_BASE_PACKAGES)) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        for (Class<?> clazz : annotationAttributes.getClassArray(ElasticJobAnnotationConstants.ENABLE_ELASTIC_JOB_ATTR_BASE_PACKAGE_CLASSES)) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }


        // 使用自定义Bean扫描器
        ElasticJobClassPathBeanDefinitionScanner scanner = new ElasticJobClassPathBeanDefinitionScanner(registry);
        if (!ObjectUtils.isEmpty(resourceLoader)) {
            scanner.setResourceLoader(resourceLoader);
        }
        scanner.resetFilters(false);

        // 如果开启智能加载，则禁用注解限制
        boolean smartLoad = annotationAttributes.getBoolean(ElasticJobAnnotationConstants.ENABLE_ELASTIC_JOB_ATTR_SMART_LOAD);
        if (!smartLoad) {
            for (Class annotation : annotationAttributes.getClassArray(ElasticJobAnnotationConstants.ENABLE_ELASTIC_JOB_ATTR_LOAD_ANNOTATIONS)) {
                scanner.addIncludeFilter(new AnnotationTypeFilter(annotation));
            }
        }
        scanner.setJobParseStrategies(jobHandlerStrategy());
        //执行扫描操作
        scanner.registerSpringScheduler(StringUtils.toStringArray(basePackages));
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    private Map<Class<? extends Annotation>, JobParseAnnotationHandlerStrategy> jobHandlerStrategy() {
        Map<Class<? extends Annotation>, JobParseAnnotationHandlerStrategy> strategies = new HashMap<>();
        Map<String, Object> parsers = ((AnnotationConfigApplicationContext) resourceLoader).getBeansWithAnnotation(JobParser.class);
        if (CollectionUtils.isEmpty(parsers)) {
            return strategies;
        }

        parsers.values().forEach(obj -> {
            if (!(obj instanceof JobParseAnnotationHandlerStrategy)) {
                return;
            }
            JobParseAnnotationHandlerStrategy strategy = (JobParseAnnotationHandlerStrategy) obj;
            //获取注解中的值
            JobParser jobParser = AnnotationUtils.findAnnotation(strategy.getClass(), JobParser.class);
            if (ObjectUtils.isEmpty(jobParser)) {
                return;
            }
            Class<? extends Annotation> annotation = jobParser.value();
            if (ObjectUtils.isEmpty(annotation)) {
                return;
            }
            if (strategies.containsKey(annotation)) {
                // 抛异常，多个解析器对应同一个注解
                throw new ElasticJobParserConflictRuntimeException("针对注解["
                        + annotation.getCanonicalName()
                        + "]发现多个处理器[" + strategies.get(annotation).getClass().getCanonicalName()
                        + "],["
                        + strategy.getClass().getCanonicalName()
                        + "]。");
            }
            strategies.put(annotation, strategy);
        });
        return strategies;
    }
}
